/**
 * 类 https://book.kotlincn.net/text/classes.html
 */
fun main131() {
    /**
     * 类声明由"类名"、
     * 类头（指定其"类型参数"、"主构造函数"等）   //可选
     * (由花括号包围的)类体 构成。               //可选, 如果一个类没有类体，可以省略花括号。
     */
    class Empty
}


/**
 * 构造函数
 */
fun main132() {
    /**
     * 1.在 Kotlin 中的一个类可以有一个"主构造函数"以及"一个或多个 次构造函数"。
     *   主构造函数是类头的一部分：它跟在类名与可选的类型参数后。
     */
    class Person1 constructor(firstName: String) { /*……*/ }

    //2.如果主构造函数没有任何注解或者可见性修饰符，可以省略这个 constructor 关键字。
    class Person2(firstName: String) { /*……*/ }

    /**
     * 3.主构造函数不能包含任何的代码。
     *   初始化的代码可以放到以 init 关键字作为前缀的初始化块（initializer blocks）中。
     *
     *   在实例初始化期间，初始化块按照它们出现在类体中的顺序执行，与属性初始化器交织在一起：
     */
    InitOrderDemo("张三")

    /**
     * 4.主构造的参数可以在初始化块中使用。它们也可以在类体内声明的属性初始化器中使用：
     */
    class Customer(name: String) {
        val customerKey = name.toUpperCase()
    }
    val cus = Customer("张三").customerKey.apply { println(this) }    //张三

    /**
     * 5.Kotlin has a concise syntax for declaring properties and initializing them from the primary constructor:
     *   Kotlin 有一个简洁的语法，用于声明属性并从主构造函数初始化它们：
     */
    class Person3(val firstName: String, val lastName: String, var age: Int)

    /**
     * 6.Such declarations can also include default values of the class properties:
     *   此类声明还可以包含类属性的默认值：
     */
    class Person4(val firstName: String, val lastName: String, var isEmployed: Boolean = true)

    /**
     * 如果构造函数有注解或可见性修饰符，这个 constructor 关键字是必需的，并且这些修饰符在它前面：
     */
    class Customer2 protected /*@Inject*/ constructor(name: String) { /*……*/ }
}
class InitOrderDemo(name: String) {
    val firstProperty = "Property1: $name".also(::println) //Property1: 张三
    init {
        println("Initializer1 $name")                      //Initializer1 张三
    }
    val secondProperty1 = "Property2: ${name.length}".also(::println)         //Property2: 2
    val secondProperty2 = "Property3: ${name.length}".also { println(this) }  //InitOrderDemo@2f0e140b
    val secondProperty3 = "Property4: ${name.length}".apply { println(this) } //Property4: 2
    init {
        println("Initializer2 ${name.length}")            //Initializer2 2
    }
}


/**
 * 次构造函数
 */
//1.类也可以声明前缀有 constructor的次构造函数：
/*private*/ class Person(val pets: MutableList<Pet> = mutableListOf())  //空参构造 & 有参构造
/*private*/ class Pet {
    constructor(owner: Person) {                            //仅有参构造
        owner.pets.add(this) // adds this pet to the list of its owner's pets
    }
}
fun main133() {
    /**
     * 2.如果类有一个主构造函数，每个次构造函数需要委托给主构造函数， 可以直接委托或者通过别的次构造函数间接委托。
     *   委托到同一个类的另一个构造函数用 this 关键字即可：
     */
    class Person2(val name: String) {
        val children: MutableList<Person2> = mutableListOf()
        constructor(name: String, parent: Person2) : this(name) {
            parent.children.add(this)
        }
    }

    /**
     * 3.请注意，初始化块中的代码实际上会成为主构造函数的一部分。
     *   对主构造函数的委托发生在访问次构造函数的第一条语句时，因此所有初始化块与属性初始化器中的代码都会在次构造函数体之前执行。
     *   即使该类没有主构造函数，这种委托仍会隐式发生，并且仍会执行初始化块：
     *   public Constructors(int i) { init()..  constructor()里剩余代码.. }
     */
    class Constructors {
        constructor(i: Int) {
            println("Constructor $i")   //2.然后走构造函数
        }

        init {
            println("Init block")       //1.先初始化
        }
    }
    val cons = Constructors(1)

    /**
     * 4.如果一个非抽象类没有声明任何（主或次）构造函数，它会有一个生成的不带参数的主构造函数。
     *   构造函数的可见性是 public。
     *   如果你不希望你的类有一个公有构造函数，那么声明一个带有非默认可见性的空的主构造函数：
     */
    class DontCreateMe private constructor() { /*……*/ }
    //val don = DontCreateMe()      //报错

    /**
     * 在 JVM 上，如果主构造函数的所有的参数都有默认值，编译器会生成一个额外的无参构造函数，它将使用默认值。
     * 这使得 Kotlin 更易于使用像 Jackson 或者 JPA 这样的通过无参构造函数创建类的实例的库。
     */
    class Customer(val customerName: String = "")
}


/**
 * 类成员
 *  可以包含：
 *      构造函数与初始化块
 *      函数
 *      属性
 *      嵌套类与内部类
 *      对象声明
 */
fun main134() {

}

/**
 * 抽象类
 */
fun main135() {
    /**
     * 1.类以及其中的某些或全部成员可以声明为 abstract。
     *   抽象成员在本类中可以不用实现。 并不需要用 open 标注抽象类或者函数。
     */
    abstract class Polygon {
        open abstract fun draw()    //提示'open'多余, 以为存在'abstract'
    }

    class Rectangle : Polygon() {
        override fun draw() {
            // draw the rectangle
        }
    }

    /**
     * 2.可以用一个抽象成员覆盖一个非抽象的开放成员。
     */
    abstract class WildShape : Polygon() {
        // Classes that inherit WildShape need to provide their own
        // draw method instead of using the default on Polygon
        abstract override fun draw()
    }
}


/**
 * 伴生对象 (就是匿名内部类 里面的方法)
 */
fun main136() {
    /**
     * 如果你需要写一个可以无需用一个类的实例来调用、但需要访问类内部的函数（例如，工厂方法），你可以把它写成该类内"对象声明"中的一员。
     * 更具体地讲，如果在你的类内声明了一个"伴生对象"， 你就可以访问其成员，只是以类名作为限定符。
     *
     * 具体见: 对象表达式与对象声明 -> 伴生对象
     */
    val instance = MyClass.create()
    //实际Java代码:
    //public final class MyClass {
    //    public static final MyClass.Factory Factory = new MyClass.Factory((DefaultConstructorMarker)null);
    //    public static final class Factory {
    //        public final MyClass create() {
    //            return new MyClass();
    //        }
    //    }
    //    MyClass instance = MyClass.Factory.create();
    //}
}
class MyClass {
    companion object Factory {      //companion [kəmˈpæ njən] n. 同伴，伴侣；（受雇照料老人或病人的）陪护；成对的物品之一；同甘共苦的伙伴；（用于书名）指南，手册；伴星；最低级爵士
                                    //                        v. 陪伴
        fun create(): MyClass = MyClass()
    }
}